#include "../lib/inc/stm32f10x_conf.h"
#include "pwm.h"
#include "io.h"

#define TO_TIM(t) (t <= PWM_TIM5 ? t : \
            (t <= PWM_TIM3_PARTIAL ? (t - PWM_TIM1_PARTIAL) : t - PWM_TIM1_FULL))


static TIM_TypeDef * tim_table[] = {TIM1, TIM2, TIM3, TIM4, TIM5};
static const uint32_t RCC_tim[] = {RCC_APB2Periph_TIM1, RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4, RCC_APB1Periph_TIM5};
uint8_t pwm_tim_pins[][4] = {
    {PA8, PA9, PA10, PA11},
    {PA0, PA1, PA2,  PA3},
    {PA6, PA7, PB0,  PB1},
    {PB6, PB7, PB8,  PB9},
    {PA0, PA1, PA2,  PA3},
    // PARTIAL REMAP
    {PA7, PB0, PB1, PNO},  // TIM1
    {PA15, PB3, PA2, PA3},  // TIM2
    {PA0, PA1, PB10, PB11},  // TIM2
    {PB4, PB5, PB0, PB1},  // TIM3
    // FULL REMAP
    {PE9, PE11, PE13, PE14},  // TIM1
    {PA15, PB3, PB10, PB11},  // TIM2
    {PC6, PC7, PC8, PC9},  // TIM3
    {PD12, PD13, PD14, PD15},  // TIM4
};

const uint32_t pwm_remap[] = {
    GPIO_PartialRemap_TIM1,
    GPIO_PartialRemap1_TIM2,
    GPIO_PartialRemap2_TIM2,
    GPIO_PartialRemap_TIM3,
    GPIO_FullRemap_TIM1,
    GPIO_FullRemap_TIM2,
    GPIO_FullRemap_TIM3,
    GPIO_Remap_TIM4,
};

static void PwmOcInit(TIM_TypeDef * tim, uint16_t chn, TIM_OCInitTypeDef * TIM_OCInitStruct) {
    switch (chn) {
        case PWM_CH1: TIM_OC1Init(tim, TIM_OCInitStruct); TIM_OC1PreloadConfig(tim, TIM_OCPreload_Enable);break;
        case PWM_CH2: TIM_OC2Init(tim, TIM_OCInitStruct); TIM_OC2PreloadConfig(tim, TIM_OCPreload_Enable);break;
        case PWM_CH3: TIM_OC3Init(tim, TIM_OCInitStruct); TIM_OC3PreloadConfig(tim, TIM_OCPreload_Enable);break;
        case PWM_CH4: TIM_OC4Init(tim, TIM_OCInitStruct); TIM_OC4PreloadConfig(tim, TIM_OCPreload_Enable);break;
    }
}

static void PwmPinInit(PWM_TIM_E tim, uint8_t chn) {
    uint8_t pin = pwm_tim_pins[tim][chn];
    IOInit(pin, Mode_AF_PP);
    if (tim >= PWM_TIM1_PARTIAL) {
        GPIO_PinRemapConfig(pwm_remap[tim - PWM_TIM1_PARTIAL],ENABLE);
    }
}

void PwmInit(PWM_TIM_E tim, PWM_CHN_E chn, uint32_t freq, uint16_t period) {
    TIM_TimeBaseInitTypeDef tim_TimeBaseInitTypeDef;
    TIM_OCInitTypeDef Tim_OCInitTypeDef;
    TIM_BDTRInitTypeDef TIM_BDTRInitStruct;

    PwmPinInit(tim, chn);
    uint8_t tim_num = (PWM_TIM_E)TO_TIM(tim);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);


    if (tim_num == PWM_TIM1)
        RCC_APB2PeriphClockCmd(RCC_tim[tim_num], ENABLE);
    else
        RCC_APB1PeriphClockCmd(RCC_tim[tim_num], ENABLE);

    tim_TimeBaseInitTypeDef.TIM_ClockDivision = TIM_CKD_DIV1;
    tim_TimeBaseInitTypeDef.TIM_CounterMode = TIM_CounterMode_Up;
    tim_TimeBaseInitTypeDef.TIM_Period = period - 1;
    tim_TimeBaseInitTypeDef.TIM_Prescaler = 72000000 / freq - 1;
    tim_TimeBaseInitTypeDef.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(tim_table[tim_num], &tim_TimeBaseInitTypeDef);

    TIM_OCStructInit(&Tim_OCInitTypeDef);
    Tim_OCInitTypeDef.TIM_OCMode = TIM_OCMode_PWM1;
    if (tim == PWM_TIM1_PARTIAL) {
        Tim_OCInitTypeDef.TIM_Pulse = period - 1;
        Tim_OCInitTypeDef.TIM_OutputNState 	= TIM_OutputNState_Enable;
        Tim_OCInitTypeDef.TIM_OCNPolarity 	= TIM_OCNPolarity_High;
        Tim_OCInitTypeDef.TIM_OCIdleState 	= TIM_OCIdleState_Reset;     
        Tim_OCInitTypeDef.TIM_OCNIdleState 	= TIM_OCIdleState_Reset;
    } else
        Tim_OCInitTypeDef.TIM_Pulse = 0;
    Tim_OCInitTypeDef.TIM_OCPolarity = TIM_OCPolarity_Low;
    Tim_OCInitTypeDef.TIM_OutputState = TIM_OutputState_Enable;

    PwmOcInit(tim_table[tim_num], chn, &Tim_OCInitTypeDef);
    TIM_ARRPreloadConfig(tim_table[tim_num], ENABLE);
    if (tim_num == PWM_TIM1) {
        TIM_BDTRStructInit(&TIM_BDTRInitStruct);
        TIM_BDTRInitStruct.TIM_OSSRState = ENABLE;
        TIM_BDTRInitStruct.TIM_OSSIState = ENABLE;
        TIM_BDTRInitStruct.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
        TIM_BDTRInitStruct.TIM_DeadTime = 0;
        TIM_BDTRInitStruct.TIM_Break = TIM_Break_Disable;
        TIM_BDTRInitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
        TIM_BDTRConfig(TIM1, &TIM_BDTRInitStruct);
        TIM_CtrlPWMOutputs(TIM1, ENABLE);
    }
    TIM_Cmd(tim_table[tim_num], ENABLE);
}

void PwmInit(PWM_TIM_CHN_E tim_chn, uint32_t freq, uint16_t period) {
    uint8_t tim = tim_chn / 4;
    uint8_t chn = tim_chn % 4;
    PwmInit((PWM_TIM_E)tim, (PWM_CHN_E)chn, freq, period);
}

void PwmSetPulse(PWM_TIM_CHN_E tim_chn, uint16_t pulse) {
    uint8_t tim = tim_chn / 4;
    uint8_t chn = tim_chn % 4;
    PwmSetPulse((PWM_TIM_E)tim, (PWM_CHN_E)chn, pulse);
}

void PwmSetPulse(PWM_TIM_E tim, PWM_CHN_E chn, uint16_t pulse) {
    uint8_t tim_num = (PWM_TIM_E)TO_TIM(tim);
    if (tim == PWM_TIM1_PARTIAL) {
        pulse = tim_table[tim_num]->ARR - pulse;
    }
    switch (chn) {
        case PWM_CH1: tim_table[tim_num]->CCR1 = pulse; break;
        case PWM_CH2: tim_table[tim_num]->CCR2 = pulse; break;
        case PWM_CH3: tim_table[tim_num]->CCR3 = pulse; break;
        case PWM_CH4: tim_table[tim_num]->CCR4 = pulse; break;
    }
}


